﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.ServiceModel;
using System.Threading;
using log4net;
using PriceLibrary.ChartService;
using System.Collections.ObjectModel;

namespace PriceLibrary.Utilities
{
    public delegate void TransactionsChanged(IList<Transaction> transactions);

    public delegate void StockBaseReceived(IList<StockBase> stockBases);

    public delegate void ReceivedAllTransactions(IList<Transaction> transactions, int countNumber, int totalcountNumber);

    public class ChartCallBack : IChartServiceCallback
    {
        #region Members and properties

        private readonly ILog _log = LogManager.GetLogger(typeof (ChartCallBack));

        private IList<string> _symbols;

        private string _connectionUri;

        public Guid Key { get; set; }

        public IList<string> Symbols
        {
            get { return _symbols; }
            set
            {
                _symbols = value;
                //TransactionsKeeped.Clear();
                //ChartService.Subscribe(Key, (List<string>) Symbols);
            }
        }

        private IChartService _chartService;
        public IChartService ChartService
        {
            get
            {
                Callback_Reconnect(null, null);
                return _chartService;
            }
            set { _chartService = value; }
        }

        private void Callback_Reconnect(object sender, EventArgs e)
        {
            int count = 0;
            while (_chartService == null ||
                (((ICommunicationObject)_chartService).State == CommunicationState.Faulted
                || ((ICommunicationObject)_chartService).State == CommunicationState.Closed)
                && count < 2
                )
            {
                try
                {
                    _chartService = ChannelFactory.CreateChannel(new InstanceContext(this));
                    if (((ICommunicationObject)_chartService).State != CommunicationState.Opened)
                    {
                        ((ICommunicationObject)_chartService).Open();
                    }
                    _chartService.SubscribeAllSymbols((List<string>) Symbols);
                    ((ICommunicationObject)_chartService).Faulted += Callback_Reconnect;
                }
                catch (Exception)
                {
                    Thread.Sleep(1000);
                    count++;
                }
                if (count >= 2)
                {
                    _log.Fatal("Khong the connect den Server!");
                    //throw new ArgumentException("Can not connect to TransactionServer!", "ChartCallBack");
                }
            }
        }

        public IList<Transaction> TransactionsKeeped { get; set; }

        public IList<StockBase> StockInfoStatics { get; set; }

        public event TransactionsChanged EndAppendTransaction;

        private void InvokeEndAppendTransaction(List<Transaction> transactions)
        {
            TransactionsChanged handler = EndAppendTransaction;
            if (handler != null) handler(transactions);
        }

        public event StockBaseReceived EndSendStockBase;

        private void InvokeEndSendStockBase(List<StockBase> infoStatics)
        {
            StockBaseReceived handler = EndSendStockBase;
            if (handler != null) handler(infoStatics);
        }

        public event ReceivedAllTransactions EndReceivedAllTransaction;

        private void InvokeEndReceivedAllTransaction(List<Transaction> transactions, int countNumber, int totalCountNumber)
        {
            ReceivedAllTransactions handler = EndReceivedAllTransaction;            
            if (handler != null) handler(transactions, countNumber, totalCountNumber);
        }

        #endregion Members and properties

        #region Contructors

        [Obsolete("Please use ChartCallBack(url). Default used: AppSettings[TransactionUrl]")]
        public ChartCallBack()
        {
            TransactionsKeeped = new List<Transaction>();
            Key = Guid.NewGuid();
            StockInfoStatics = new List<StockBase>();
            _symbols = new List<string>();

            if (ConfigurationManager.AppSettings["TransactionUrl"] != null)
            {
                _connectionUri = ConfigurationManager.AppSettings["TransactionUrl"];
                Init(ConfigurationManager.AppSettings["TransactionUrl"]);
            }
        }

        public ChartCallBack(string url)
        {
            TransactionsKeeped = new List<Transaction>();
            Key = Guid.NewGuid();
            StockInfoStatics = new List<StockBase>();
            _symbols = new List<string>();

            if (url != null)
            {
                _connectionUri = url;
                Init(url);
            }
        }
        #endregion Contructors

        protected void Init(string uri)
        {
            NetTcpBinding netTcpBinding = new NetTcpBinding(SecurityMode.None);
            netTcpBinding.MaxReceivedMessageSize = netTcpBinding.MaxBufferSize = int.MaxValue;
            netTcpBinding.MaxConnections = 30;

            netTcpBinding.OpenTimeout = TimeSpan.FromMinutes(1.0d);
            netTcpBinding.CloseTimeout = TimeSpan.FromMinutes(1.0d);
            netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;

            EndpointAddress endpointAddress = new EndpointAddress(uri);
            _channelFactory = new DuplexChannelFactory<IChartService>(typeof(PriceCallBack), netTcpBinding, endpointAddress);

            ChannelFactory.Open();
        }

        private DuplexChannelFactory<IChartService> _channelFactory;
        protected DuplexChannelFactory<IChartService> ChannelFactory
        {
            get
            {
                if (_channelFactory==null)
                {
                    Init(this._connectionUri);
                }
                return _channelFactory;
            }
            set { _channelFactory = value; }
        }

        #region Implementation of IChartServiceCallback

        public void AppendTransactions(List<Transaction> transactions)
        {
            foreach (Transaction transaction in transactions)
            {
                TransactionsKeeped.Add(transaction);
            }
            InvokeEndAppendTransaction(transactions);
            _log.Info("End of Append transactions!");
        }

        public void SendBasePrices(List<StockBase> infoStatics)
        {
            //StockInfoStatics = infoStatics;
            foreach (StockBase infoStatic in infoStatics)
            {
                StockInfoStatics.Add(infoStatic);
            }
            InvokeEndSendStockBase(infoStatics);
            _log.Info("End send base prices");
        }


        public void SendAllTransactions(List<Transaction> transactions, int countNumber, int totalCountNumber)
        {
            foreach (Transaction transaction in transactions)
            {
                TransactionsKeeped.Add(transaction);
            }
            InvokeEndReceivedAllTransaction(transactions, countNumber, totalCountNumber);
            _log.Info("End of Reveived all transactions!");
        }

        #endregion

        #region Tickers

        public void Clear()
        {
            ChartService.UnsubscribeSymbols((List<string>)Symbols);
            Symbols.Clear();
            TransactionsKeeped.Clear();
            _log.Info("Chart Callback is clearing all tickers!");
        }

        public void Add(string ticker)
        {
            if (!Symbols.Contains(ticker))
            {
                Symbols.Add(ticker);
                ChartService.SubscribeNewSymbol(ticker);
                _log.Info(string.Format("Adds new ticker {0} from client!", ticker));
            }
        }

        public void Remove(string ticker)
        {
            if (Symbols.Contains(ticker))
            {
                Symbols.Remove(ticker);
                ChartService.UnsubscribeSymbols(new List<string>{ticker});
                _log.Info(string.Format("Removes ticker {0} from client!", ticker));
            }
        }

        #endregion
    }
}